#ifndef PLUGINMANAGER_H
#define PLUGINMANAGER_H

#include <vector>
#include <map>
#include <set>
#include <wx/dynarray.h>
#include "globals.h"
#include "manager.h"

class M_Code_Plugin;
class cbCompilerPlugin;
class cbMimePlugin;
class cbConfigurationPanel;
class M_Code_Project;
class wxDynamicLibrary;
class wxMenuBar;
class wxMenu;
class CodeBlocksEvent;
class TiXmlDocument;
class FileTreeData;
struct PluginInfo;

typedef void( *PluginSDKVersionProc )( int*, int*, int* );
typedef M_Code_Plugin*( *CreatePluginProc )();
typedef void( *FreePluginProc )( M_Code_Plugin* );

struct PluginInfo {
  wxString name;
  wxString title;
  wxString description;
};

struct PluginElement {
  PluginInfo info;
  wxString fileName;
  wxDynamicLibrary* library;
  FreePluginProc freeProc;
  M_Code_Plugin* plugin;
};

WX_DEFINE_ARRAY( PluginElement*, PluginElementsArray );
WX_DEFINE_ARRAY( M_Code_Plugin*, PluginsArray );
WX_DEFINE_ARRAY( cbConfigurationPanel*, ConfigurationPanelsArray );

class PluginManager : public Mgr<PluginManager>, public wxEvtHandler {
  public:
    typedef std::vector<cbCompilerPlugin*> CompilerPlugins;
  public:
    friend class Mgr<PluginManager>;
    friend class Manager;
    void CreateMenu( wxMenuBar* menuBar );
    void ReleaseMenu( wxMenuBar* menuBar );
    void RegisterPlugin( const wxString& name, CreatePluginProc createProc, FreePluginProc freeProc, PluginSDKVersionProc versionProc );
    int ScanForPlugins( const wxString& path );
    bool LoadPlugin( const wxString& pluginName );
    void LoadAllPlugins();
    void UnloadAllPlugins();
    void UnloadPlugin( M_Code_Plugin* plugin );
    int ExecutePlugin( const wxString& pluginName );
    bool AttachPlugin( M_Code_Plugin* plugin, bool ignoreSafeMode = false );
    bool DetachPlugin( M_Code_Plugin* plugin );
    bool InstallPlugin( const wxString& pluginName, bool forAllUsers = true, bool askForConfirmation = true );
    bool UninstallPlugin( M_Code_Plugin* plugin, bool removeFiles = true );
    bool ExportPlugin( M_Code_Plugin* plugin, const wxString& filename );
    const PluginInfo* GetPluginInfo( const wxString& pluginName );
    const PluginInfo* GetPluginInfo( M_Code_Plugin* plugin );
    const PluginElementsArray& GetPlugins() const { return m_Plugins; }
    PluginElement* FindElementByName( const wxString& pluginName );
    M_Code_Plugin* FindPluginByName( const wxString& pluginName );
    M_Code_Plugin* FindPluginByFileName( const wxString& pluginFileName );
    const CompilerPlugins& GetCompilerPlugins() const { return m_CompilerPlugins; }
    cbCompilerPlugin* GetFirstCompiler() const;
    PluginsArray GetToolOffers();
    PluginsArray GetMimeOffers();
    PluginsArray GetDebuggerOffers();
    PluginsArray GetCodeCompletionOffers();
    PluginsArray GetSmartIndentOffers();
    PluginsArray GetOffersFor( PluginType type );
    void AskPluginsForModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data = nullptr );
    cbMimePlugin* GetMIMEHandlerForFile( const wxString& filename );
    void GetConfigurationPanels( int group, wxWindow* parent, ConfigurationPanelsArray& arrayToFill );
    void GetProjectConfigurationPanels( wxWindow* parent, M_Code_Project* project, ConfigurationPanelsArray& arrayToFill );
    int Configure();
    void SetupLocaleDomain( const wxString& DomainName );
    void NotifyPlugins( CodeBlocksEvent& event );
    void NotifyPlugins( CodeBlocksDockEvent& event );
    void NotifyPlugins( CodeBlocksLayoutEvent& event );
    static void SetSafeMode( bool on ) { s_SafeMode = on; }
    static bool GetSafeMode() { return s_SafeMode; }
  private:
    PluginManager();
    ~PluginManager();
    void OnScriptMenu( wxCommandEvent& event );
    void OnScriptModuleMenu( wxCommandEvent& event );
    bool ReadManifestFile( const wxString& pluginFilename, const wxString& pluginName = wxEmptyString,
                           PluginInfo* infoOut = nullptr );
    bool ExtractFile( const wxString& bundlename, const wxString& src_filename, const wxString& dst_filename,
                      bool isMandatory = true );
    PluginElementsArray m_Plugins;
    wxString m_CurrentlyLoadingFilename;
    wxDynamicLibrary* m_pCurrentlyLoadingLib;
    TiXmlDocument* m_pCurrentlyLoadingManifestDoc;
    struct PluginRegistration {
      PluginRegistration() : createProc( nullptr ), freeProc( nullptr ), versionProc( nullptr ) { }
      PluginRegistration( const PluginRegistration& rhs )
        : name( rhs.name ), createProc( rhs.createProc ), freeProc( rhs.freeProc ), versionProc( rhs.versionProc ), info( rhs.info ) { }
      wxString name;
      CreatePluginProc createProc;
      FreePluginProc freeProc;
      PluginSDKVersionProc versionProc;
      PluginInfo info;
    };
    std::vector<PluginRegistration> m_RegisteredPlugins;
    CompilerPlugins m_CompilerPlugins;
    static bool s_SafeMode;
    DECLARE_EVENT_TABLE()
};

bool cbHasRunningCompilers( const PluginManager *manager );
void cbStopRunningCompilers( PluginManager *manager );

#endif
